﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

using LINQToolKit.Mapping;
using LINQToolKit.Processors;
using LINQToolKit.Web;

namespace LINQToolKit
{
    public sealed class EntityTable<T> : IEntityTable<T>, IQueryable<T>, IOrderedQueryable<T>
    {
        private MetaEntity _metaModel;
        private IEntityProvider _provider;

        public EntityTable(MetaEntity meta, IEntityProvider provider)
        {
            meta.AssertNull("meta");
            provider.AssertNull("provider");
            this._metaModel = meta;
            this._provider = provider;
        }

        public void Insert(T entity)
        {
            entity.AssertNull("entity");

            if (!this._metaModel.IsInsertable)
            {
                throw new InvalidOperationException();
            }

            var result = this.EntityProvider.Insert(new Uri(this._metaModel.InsertUri), entity);

            if (!result.Success)
            {
                throw new RequestException(result);
            }
        }

        public void Delete(string id)
        {
            id.AssertNull("id");

            if (!this._metaModel.IsDeleteable)
            {
                throw new InvalidOperationException();
            }

            var deleteUrl = string.Format(this._metaModel.DeleteUri, id);
            var result = this.EntityProvider.Delete(typeof(T), new Uri(deleteUrl));

            if (!result.Success)
            {
                throw new RequestException(result);
            }
        }

        public void Update(string id, T entity)
        {
            id.AssertNull("id");

            if (!this._metaModel.IsUpdateable)
            {
                throw new InvalidOperationException();
            }

            string updateUri = string.Format(this._metaModel.UpdateUri, id);
            var result = this.EntityProvider.Update(new Uri(updateUri), entity);

            if (!result.Success)
            {
                throw new RequestException(result);
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            if (!string.IsNullOrEmpty(this._metaModel.SourceUri))
            {
                var result =
                    this._provider.Select(typeof(T), new Uri(this._metaModel.SourceUri));

                return (IEnumerator<T>)result.GetEnumerator();
            }

            return new List<T>().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public Type ElementType
        {
            get { return typeof(T); }
        }

        public Expression Expression
        {
            get 
            {
                return Expression.Constant(this);
            }
        }

        public IQueryProvider Provider
        {
            get { return this._provider; }
        }

        public T GetByID(string id)
        {
            id.AssertNull("id");
            string selectUrl = string.Format(this._metaModel.EntryUri, id);

            return (T)this.EntityProvider.SelectOne(typeof(T), new Uri(selectUrl));
        }

        public IEntityProvider EntityProvider
        {
            get { return this._provider; }
        }
    }
}
